今天的筆記主要是以 kotlin 會透過什麼方式來實現委託,以下如有解釋不清或是描述錯誤的地方還請大家多多指教:
先來淺談什麼是 delegate,Delegate Pattern 是設計模式中的其中一項,是接受請求的對象將請求的事情委託給另一個對象來處理,是什麼意思呢?直接來看看範例會更清楚:
interface Base {
fun count()
}
class Counter(val money: Int) : Base {
override fun count() { print(money*5) }
}
class Bank(val counter: Base) : Base by counter
fun main() {
val b = Counter(20)
Bank(b).count()
// get 100
}
class Bank
implement Base
這個 interface,但實作的部分交由 Counter
來處理,Kotlin 是使用 by
來執行委託:
當請求的對象也實作方法時,實作的內容會覆蓋掉被委託者做的事情:
interface Base {
fun count()
fun countAgain()
}
class Counter(val money: Int) : Base {
override fun count() { print(money*5) }
override fun countAgain() { print(money*10) }
}
class Bank(val counter: Base) : Base by counter {
override fun count() { print("abc") }
}
fun main() {
val b = Counter(20)
Bank(b).count()
Bank(b).countAgain()
// origin: 100200
// override: abc100
}
interface Base {
val message: String
fun count()
}
class Counter(val money: Int) : Base {
override val message = "Counter count money : $money"
override fun count() { println(message) }
}
class Bank(val counter: Base) : Base by counter {
// This property is not accessed from counter's implementation of `count`
override val message = "Message of Bank"
}
fun main() {
val b = Counter(20)
val bank = Bank(b)
bank.count()
println(bank.message)
// Counter count money : 20
// Message of Bank
}
Bank 覆寫了 message 的值,並不是覆寫方法,所以執行 count() 的時候還是以 Counter 覆寫的值為主。
有些屬性雖然也是可以在需要用到它的時候再建立出來,但也可以透過委託的方式先進行實作,等到需要用的時候再使用它,以下是語法使用方式:
val/var <property name>: <Type> by <expression>
屬性委託不需要實作任何 interface,但委託的的對象需要提供 getValue()
,如果是 var
還是需要提供 setValue()
:
class Delegate {
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
return "$thisRef, thank you for delegating '${property.name}' to me!"
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
println("$value has been assigned to '${property.name}' in $thisRef.")
}
}
class Example {
// e.g.
var name: String by Delegate()
print(name) // get 'Example, thank you for delegating name to me!'
name = "my name"
print(name) // get 'my name has been assigned to name in Example'
}
thisRef
為進行委託的對象,他的 type 需要跟 property owner 依樣或是 supertypeproperty
的 type u 一定要是 KProperty<*>
或是 supertypeKotlin 本身內置一些透過工廠方法實現屬性委託的方法:
val lazyValue: String by lazy {
println("computed!") // 此動作只會在第一次的時候執行
"Hello"
}
fun main() {
println(lazyValue)
println(lazyValue)
// computed!
// Hello
// Hello
}
class User {
var name: String by Delegates.observable("default") {
prop, old, new ->
println("$old -> $new")
}
}
fun main() {
val user = User()
user.name = "Nami"
user.name = "Luffy"
// first line print: default -> Nami
// second line print: Nami -> Luffy
}
class User(val map: Map<String, Any?>) {
val name: String by map
val age: Int by map
}
fun main(args: Array<String>) {
val user = User(mapOf(
"name" to "Luffy",
"age" to 25
))
println(user.name) // get "Luffy"
println(user.age) // get 25
}
自 Kotlin 1.4 後,屬性委託可以委託給另一個屬性,上面看到的範例都是委託給另一個 class 或是 function,可使用的情境有:
在要委託的屬性前加上 ::
,根據以上三個使用情境,::
前綴加的名稱會有所不同:
var top: Int = 0
class ClassWithDelegate(val anotherClassInt: Int)
class User(var member: Int, val another: ClassWithDelegate) {
var delegatedToMember: Int by this::member
var delegatedToTopLevel: Int by ::top
val delegatedToAnotherClass: Int by another::anotherClassInt
}
var User.extDelegated: Int by ::top
::top
this::member
another::anotherClassInt
我們來看看底層的解析規則吧!compiler 會將每個委託屬性生成一個輔助屬性並將其委託給這個輔助屬性,以下面的範例來看,屬性 name
在 compile 生成了隱藏的屬性 name$delegate
,而訪問器的代碼簡單的委託給這個附加的屬性。
class User {
var name: Type by CustomDelegate()
}
// this code is generated by the compiler instead:
class User {
private val name$delegate = CustomDelegate()
var name: Type
get() = name$delegate.getValue(this, this::name)
set(value: Type) = name$delegate.setValue(this, this::name, value)
}
compiler 生成了所有有關 name
的資訊:第一個 argument 的 thi
s 是 class c 的 instance,this::name
是 KProperty
的反射對象,描述 name 本身。
那屬性委託給另一個屬性 compile 後會變怎麼樣呢?
class User<Type> {
private var impl: Type = ...
var prop: Type by ::impl
}
// this code is generated by the compiler instead:
class User<Type> {
private var impl: Type = ...
var prop: Type
get() = impl
set(value) {
impl = value
}
// This method is needed only for reflection
fun getProp$delegate(): Type = impl
}
pro
直接調用 impl
的值,跳過 getValue
及 setValue
,因此不需要 KProperty
那專案會有那些地方會使用到 delegate 呢?
我會透過 by lazy 來初始化已實作好的 Adapter 以及 by viewModel 來產出 ViewModel:
class HomeFragment : Fragment() {
private val viewModel by viewModels<MyViewModelName>()
private val listAdapter by lazy { MyAdapterName() }
...
}